package cn.wolfcode.shop.service.impl;

import cn.wolfcode.shop.common.BusinessException;
import cn.wolfcode.shop.consts.MQConstents;
import cn.wolfcode.shop.domain.OrderInfo;
import cn.wolfcode.shop.mapper.OrderInfoMapper;
import cn.wolfcode.shop.msg.SeckillServerCodeMsg;
import cn.wolfcode.shop.service.IOrderInfoService;
import cn.wolfcode.shop.service.ISeckillGoodService;
import cn.wolfcode.shop.service.ISeckillOrderService;
import cn.wolfcode.shop.util.IdGenerateUtil;
import cn.wolfcode.shop.util.RedisKeys;
import cn.wolfcode.shop.vo.SeckillGoodVO;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Service
public class OrderInfoServiceImpl implements IOrderInfoService {
    @Autowired
    private ISeckillGoodService seckillGoodService;
    @Autowired
    private ISeckillOrderService seckillOrderService;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    // DML 操作，要保证原子性等，要加上事务，显示的指定Exception
    @Transactional(rollbackFor = Exception.class)
    public String doSeckill(Long seckillId, Long userId) {
        // 做秒杀操作，包括

        // 1.减库存   t_seckill_goods
        int rows = seckillGoodService.decrStockCount(seckillId);
        if (rows <= 0) {
            // 返回受影响的行数, == 0， 表示修改失败
            throw new BusinessException(SeckillServerCodeMsg.STOCKCOUNT_OVER_ERROR);
        }

        // 2.创建普通订单（秒杀订单依赖普通订单）  t_order_info
        String orderNo = this.creatOrder(seckillId, userId);
        // 3.创建秒杀订单  t_seckill_order
        seckillOrderService.creatSeckillOrder(seckillId, userId, orderNo);
        return orderNo;
    }

    @Override
    public OrderInfo findByIdAndUserId(String orderNo, Long userId) {
        if (orderNo == null || userId == null) {
            throw new BusinessException(SeckillServerCodeMsg.OPS_ERROR);
        }
        OrderInfo orderInfo = orderInfoMapper.selectByIdAndUserId(orderNo, userId);
        return orderInfo;
    }

    // 查订单是否超时未支付
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkOrderTimeOut(String orderNo, Long seckillId) {
        // 1. 检查订单是否超时未支付
        // 2.1 如果已经超时：需要将当前订单的状态修改为超时未支付：更新操作 update xx set status = 4 where status = 0
        int ret = orderInfoMapper.updateTimeoutStatus(orderNo);
        if (ret > 0) {  // 修改成功：超时未支付的订单
            // 2.2 回补MySQL中的库存
            orderInfoMapper.incrStockCount(seckillId);
            // 2.3 回补Redis中的库存，将MySQL中回补好的库存同步到Redis中
            // 调用抽离出去的方法完成上面的操作
            incrStockCountForRedis(seckillId);
            // 2.4 清除本地售完标记
            // 2.4.1 发送广播消息，通知集群的所有节点清除自己的售完标记
            rocketMQTemplate.syncSend(MQConstents.CLEAN_STOCKCOUNT_FLAG_DEST, seckillId);
        }
    }


    // 回补 Redis 库存，清除本地缓存的售完标记
    @Override
    public void handlerFailedseckill(Long seckillId) {
        // 调用方法完成上面的操作
        incrStockCountForRedis(seckillId);
        // 清除本地售完标记
        //  发送广播消息，通知集群的所有节点清除自己的售完标记
        rocketMQTemplate.syncSend(MQConstents.CLEAN_STOCKCOUNT_FLAG_DEST, seckillId);
    }

    /**
     * 抽离出来公用的方法：负责回补 Redis 库存，清除本地缓存的售完标记
     *
     * @param seckillId
     */
    private void incrStockCountForRedis(Long seckillId) {
        // 2.3 回补Redis中的库存，将MySQL中回补好的库存同步到Redis中
        String stockCountStr = (String) redisTemplate.opsForHash()
                .get(RedisKeys.SECKILL_GOOD_STOCKCOUNT_HASH.join(), seckillId + "");
        if (!StringUtils.isEmpty(stockCountStr)) {
            int stockCount = Integer.parseInt(stockCountStr);   // 库存
            int updateStockCount = 1;
            if (stockCount > 0) {
                // 如果库存还大于0，就是原来的库存 + 1，否则设置为 1
                updateStockCount = updateStockCount + stockCount;
            }
            // 将新库存设置到Redis中，更新Redis中的库存
            redisTemplate.opsForHash()
                    .put(RedisKeys.SECKILL_GOOD_STOCKCOUNT_HASH.join(), seckillId + "", updateStockCount + "");
        }
    }


    // 2.创建普通订单
    private String creatOrder(Long seckillId, Long userId) {
        //SeckillGoodVO vo = seckillGoodService.findById(seckillId);
        SeckillGoodVO vo = seckillGoodService.findByCache(seckillId);
        OrderInfo orderInfo = new OrderInfo();
        // 分布式唯一主键
        String orderNo = "";
        long id = IdGenerateUtil.get().nextId();
        orderNo = orderNo + id;  // 主键一般用 String
        // generatAll 插件快速生成
        orderInfo.setOrderNo(orderNo);
        orderInfo.setGoodCount(1);   // 商品数量
        orderInfo.setUserId(userId);
        orderInfo.setGoodId(vo.getGoodId());
        orderInfo.setGoodName(vo.getGoodName());
        orderInfo.setGoodImg(vo.getGoodImg());
        orderInfo.setGoodCount(vo.getStockCount());
        orderInfo.setGoodPrice(vo.getGoodPrice());
        orderInfo.setSeckillPrice(vo.getSeckillPrice());
        orderInfo.setCreateDate(vo.getEndDate());

        // 保存订单对象
        orderInfoMapper.insert(orderInfo);

        return orderNo;
    }
}
